package cc.xpcas.raft.network.jvm;

import cc.xpcas.raft.codec.Codec;
import cc.xpcas.raft.message.Message;
import cc.xpcas.raft.network.Endpoint;
import cc.xpcas.raft.network.Packet;
import lombok.Getter;

/**
 * @author xp
 */
public class JvmEndpoint implements Endpoint<JvmAddress> {

    private final Object senderLock = new Object();

    @Getter
    private volatile boolean isSendable = false;

    private final Object receiverLock = new Object();

    @Getter
    private volatile boolean isReceivable = false;

    @Getter
    private JvmAddress addressSender;

    @Getter
    private JvmAddress addressReceiver;

    private Codec codec;

    @Override
    public boolean openSender(JvmAddress address, Codec codec) {
        synchronized (senderLock) {
            if (isSendable) return false;
            isSendable = true;
        }
        this.addressSender = address;
        this.codec = codec;
        return true;
    }

    @Override
    public void closeSender() {
        synchronized (senderLock) {
            if (!isSendable) return;
            isSendable = false;
        }
    }

    @Override
    public boolean openReceiver(JvmAddress address, Codec codec) {
        synchronized (receiverLock) {
            if (isReceivable) return false;
            isReceivable = true;
        }
        this.addressReceiver = address;
        this.codec = codec;
        JvmHub.getInstance().addListener(this.addressReceiver);
        return true;
    }

    @Override
    public void closeReceiver() {
        synchronized (receiverLock) {
            if (!isReceivable) return;
            isReceivable = false;
        }
        JvmHub.getInstance().removeListener(addressReceiver);
    }

    @Override
    public void send(Packet packet) {
        if (!isSendable) return;
        try {
            byte[] bytes = codec.encode(packet.getMessage());
            JvmHub.getInstance().sendTo(addressSender, (JvmAddress) packet.getDest(), bytes);
        } catch (InterruptedException e) {
            // ignore it
        }
    }

    @Override
    public Packet receive() {
        if (!isReceivable) return null;
        try {
            JvmPacket jvmPacket = JvmHub.getInstance().receiveFrom(addressReceiver);
            Message message = (Message) codec.decode(jvmPacket.getBytes());
            return new Packet(jvmPacket.getRemote(), addressReceiver, message);
        } catch (InterruptedException e) {
            // ignore it
            return null;
        }
    }
}
